package service

import (
	sqlc "bgs/db/sqlc"
	"bgs/grpc/gen/proxy2/wxpay"
	"bgs/util"
	"context"
	"fmt"
	"strconv"
	"time"
)

// 目前PlatformCommission,DistributorCommission,BonusAmount不会分账回退,因为它们必定在售后截止的场景下分账
// CreateRollbackParam 创建本地分账回退请求参数
type CreateRollbackParam struct {
	BrandID               int64
	OrderID               int64
	OrderSerialNumber     string
	AfterSaleID           int64
	AfterSaleSerialNumber string
	LedgerRequestPtr      *sqlc.LedgerRequest
	SubmchProfitAmount    int64
	PlatformCommission    int64
	DistributorCommission int64
	BonusAmount           int64
	ProfitTrigger         sqlc.EmProfitTrigger
}

// CreateRollback 创建本地分账请求 (主动)
func (s *LedgerServiceImp) CreateRollback(ctx context.Context, q *sqlc.Queries, p CreateRollbackParam) (
	ledgerRollback sqlc.LedgerRollback,
	ledgerRollbackDetails []*sqlc.LedgerDetail,
	err error) {
	slog := util.NewContextLogger(ctx, s.slog)
	brandID := p.BrandID

	slog.Infof("准备创建订单分账回退-AfterSaleSerialNumber(%s), SubmchProfitAmount:(%d), PlatformCommission:(%d), DistributorCommission:(%d), BonusAmount:(%d)",
		p.AfterSaleSerialNumber, p.SubmchProfitAmount, p.PlatformCommission, p.DistributorCommission, p.BonusAmount)
	ledgerRollback, err = q.UpsertLedgerRollback(ctx, sqlc.UpsertLedgerRollbackParams{
		BrandID:                   brandID,
		OrderID:                   p.OrderID,
		OrderSerialNumber:         p.OrderSerialNumber,
		AfterSaleID:               p.AfterSaleID,
		AfterSaleSerialNumber:     p.AfterSaleSerialNumber,
		LedgerRequestID:           p.LedgerRequestPtr.ID,
		LedgerRequestSerialNumber: p.LedgerRequestPtr.SerialNumber,
		SerialNumber:              s.generateLedgerSerialNumber("LB", p.OrderSerialNumber),
		SubmchProfitAmount:        p.SubmchProfitAmount,
		PlatformCommission:        p.PlatformCommission,
		DistributorCommission:     p.DistributorCommission,
		BonusAmount:               p.BonusAmount,
		ProfitTrigger:             string(p.ProfitTrigger),
	})
	if err != nil {
		return
	}
	slog.Infof("创建订单分账回退请求成功")

	ledgerDetailPtrsOfRequest, err := s.ListLedgerDetailsByRequestID(ctx, q, p.LedgerRequestPtr.ID)
	if err != nil {
		return
	}
	slog.Infof("获取订单分账请求明细")

	if p.SubmchProfitAmount > 0 {
		ledgerDetailPtrsOfRequestForSubmchProfit, ok := s.FindLedgerDetailPtr(ledgerDetailPtrsOfRequest, func(ld *sqlc.LedgerDetail) bool {
			return ld.LedgerRequestID.Valid &&
				ld.Category.Valid && ld.Category.String == string(sqlc.EmLedgerCategorySubmchProfit)
		})
		if !ok {
			err = fmt.Errorf("无法找到子商户的分账请求记录")
			return
		}
		ledgerDetail, err2 := q.CreateLedgerDetail(ctx, sqlc.CreateLedgerDetailParams{
			Category:                   sqlc.NSFromString(string(sqlc.EmLedgerCategoryRollbackSubmchProfit)),
			LedgerRollbackID:           sqlc.NInt64FromInt64(ledgerRollback.ID),
			LedgerRollbackSerialNumber: sqlc.NSFromString(ledgerRollback.SerialNumber),
			BrandID:                    ledgerRollback.BrandID,
			OrderID:                    ledgerRollback.OrderID,
			OrderSerialNumber:          ledgerRollback.OrderSerialNumber,
			AfterSaleID:                sqlc.NInt64FromInt64(ledgerRollback.AfterSaleID),
			AfterSaleSerialNumber:      sqlc.NSFromString(ledgerRollback.AfterSaleSerialNumber),
			Receiver:                   ledgerDetailPtrsOfRequestForSubmchProfit.Receiver,
			Amount:                     0 - p.SubmchProfitAmount, // <0
			Remark:                     sqlc.NSFromString("平台佣金回退"),
		})
		if err2 != nil {
			err = err2
			return
		}

		ledgerRollbackDetails = append(ledgerRollbackDetails, &ledgerDetail)
	}

	if p.PlatformCommission > 0 {

		ledgerDetailPtrsOfRequestForPlatformCommission, ok := s.FindLedgerDetailPtr(ledgerDetailPtrsOfRequest, func(ld *sqlc.LedgerDetail) bool {
			return ld.LedgerRequestID.Valid &&
				ld.Category.Valid && ld.Category.String == string(sqlc.EmLedgerCategoryPlatformCommission)
		})
		if !ok {
			err = fmt.Errorf("无法找到平台的分账请求记录")
			return
		}

		ledgerDetail, err2 := q.CreateLedgerDetail(ctx, sqlc.CreateLedgerDetailParams{
			Category:                   sqlc.NSFromString(string(sqlc.EmLedgerCategoryRollbackPlatformCommission)),
			LedgerRollbackID:           sqlc.NInt64FromInt64(ledgerRollback.ID),
			LedgerRollbackSerialNumber: sqlc.NSFromString(ledgerRollback.SerialNumber),
			BrandID:                    ledgerRollback.BrandID,
			OrderID:                    ledgerRollback.OrderID,
			OrderSerialNumber:          ledgerRollback.OrderSerialNumber,
			AfterSaleID:                sqlc.NInt64FromInt64(ledgerRollback.AfterSaleID),
			AfterSaleSerialNumber:      sqlc.NSFromString(ledgerRollback.AfterSaleSerialNumber),
			Receiver:                   ledgerDetailPtrsOfRequestForPlatformCommission.Receiver,
			Amount:                     0 - p.PlatformCommission, // <0
			Remark:                     sqlc.NSFromString("平台佣金回退"),
		})
		if err2 != nil {
			err = err2
			return
		}

		ledgerRollbackDetails = append(ledgerRollbackDetails, &ledgerDetail)
	}
	if p.DistributorCommission > 0 {
		ledgerDetailPtrsOfRequestForDistributorCommission, ok := s.FindLedgerDetailPtr(ledgerDetailPtrsOfRequest, func(ld *sqlc.LedgerDetail) bool {
			return ld.LedgerRequestID.Valid &&
				ld.Category.Valid && ld.Category.String == string(sqlc.EmLedgerCategoryOrderDistributorCommission)
		})
		if !ok {
			err = fmt.Errorf("无法找到分销方的分账请求记录")
			return
		}
		ledgerDetail, err2 := q.CreateLedgerDetail(ctx, sqlc.CreateLedgerDetailParams{
			Category:                   sqlc.NSFromString(string(sqlc.EmLedgerCategoryRollbackOrderDistributorCommission)),
			LedgerRollbackID:           sqlc.NInt64FromInt64(ledgerRollback.ID),
			LedgerRollbackSerialNumber: sqlc.NSFromString(ledgerRollback.SerialNumber),
			BrandID:                    ledgerRollback.BrandID,
			OrderID:                    ledgerRollback.OrderID,
			OrderSerialNumber:          ledgerRollback.OrderSerialNumber,
			AfterSaleID:                sqlc.NInt64FromInt64(ledgerRollback.AfterSaleID),
			AfterSaleSerialNumber:      sqlc.NSFromString(ledgerRollback.AfterSaleSerialNumber),
			Receiver:                   ledgerDetailPtrsOfRequestForDistributorCommission.Receiver,
			Amount:                     0 - p.DistributorCommission,
			Remark:                     sqlc.NSFromString("品牌分销订单佣金回退"),
		})
		if err2 != nil {
			err = err2
			return
		}
		ledgerRollbackDetails = append(ledgerRollbackDetails, &ledgerDetail)
	}
	if p.BonusAmount > 0 {

		ledgerDetailPtrsOfRequestForBonusProfit, ok := s.FindLedgerDetailPtr(ledgerDetailPtrsOfRequest, func(ld *sqlc.LedgerDetail) bool {
			return ld.LedgerRequestID.Valid &&
				ld.Category.Valid && ld.Category.String == string(sqlc.EmLedgerCategoryOrderBonusProfit)
		})
		if !ok {
			err = fmt.Errorf("无法找到代言人的分账请求记录")
			return
		}

		ledgerDetail, err2 := q.CreateLedgerDetail(ctx, sqlc.CreateLedgerDetailParams{
			Category:                   sqlc.NSFromString(string(sqlc.EmLedgerCategoryRollbackOrderBonusProfit)),
			LedgerRollbackID:           sqlc.NInt64FromInt64(ledgerRollback.ID),
			LedgerRollbackSerialNumber: sqlc.NSFromString(ledgerRollback.SerialNumber),
			BrandID:                    ledgerRollback.BrandID,
			OrderID:                    ledgerRollback.OrderID,
			OrderSerialNumber:          ledgerRollback.OrderSerialNumber,
			AfterSaleID:                sqlc.NInt64FromInt64(ledgerRollback.AfterSaleID),
			AfterSaleSerialNumber:      sqlc.NSFromString(ledgerRollback.AfterSaleSerialNumber),
			Receiver:                   ledgerDetailPtrsOfRequestForBonusProfit.Receiver,
			Amount:                     0 - p.BonusAmount,
			Remark:                     sqlc.NSFromString("订单奖励金回退"),
		})
		if err2 != nil {
			err = err2
			return
		}
		ledgerRollbackDetails = append(ledgerRollbackDetails, &ledgerDetail)
	}
	slog.Infof("批量创建订单分账回退的分账明细成功")
	return
}

// CloseRollback 关闭本地分账回退 (主动)
func (s *LedgerServiceImp) closeRollback(ctx context.Context, q *sqlc.Queries, afterSaleSerialNumber string) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	err = q.CloseLedgerRollbackByAfterSaleSerialNumber(ctx, afterSaleSerialNumber)
	if err != nil {
		return err
	}
	slog.Infof("关闭本地分账回退成功")
	return
}

// FinishRollback 完成本地分账回退 (主动)
func (s *LedgerServiceImp) finishRollback(ctx context.Context, q *sqlc.Queries, afterSaleSerialNumber string, finished time.Time) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	err = q.FinishLedgerRollbackByAfterSaleSerialNumber(ctx, sqlc.FinishLedgerRollbackByAfterSaleSerialNumberParams{
		AfterSaleSerialNumber: afterSaleSerialNumber,
		Finished:              sqlc.NTimeFromTime(finished),
	})
	if err != nil {
		return
	}
	slog.Infof("完成本地分账回退成功")
	return
}

// RollbackToProxy rollback ledger to remote proxy (主动)
func (s *LedgerServiceImp) RollbackToProxy(ctx context.Context, q *sqlc.Queries, ledgerRollbackPtr *sqlc.LedgerRollback, ledgerRollDetailPtr []*sqlc.LedgerDetail) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	// 注意:目前仅子商户分账需要回退,因此必然一个回退对应一个回退明细
	receiver := s.ConvLedgerDetailPtrToProfitReceiverPtr(ledgerRollDetailPtr[0])
	returnMchId := receiver.ReceiverAccount
	wxLedgerReturnNo, err := s.submitProfitReturn(ctx, ledgerRollbackPtr, returnMchId)
	if err != nil {
		return
	}
	slog.Infof("提交订单分账回退成功")
	err = q.UpdateLedgerRollbackWxLedgerReturnNo(ctx, sqlc.UpdateLedgerRollbackWxLedgerReturnNoParams{
		ID:               ledgerRollbackPtr.ID,
		Submitted:        sqlc.NTimeFromTime(time.Now()),
		WxLedgerReturnNo: sqlc.NSFromString(wxLedgerReturnNo),
	})
	if err != nil {
		return
	}
	slog.Infof("提交订单分账回退成功")
	return
}

// DoAfterWxpayProfitReturnFinishParam wxpay profit return param
type DoAfterWxpayProfitReturnFinishParam struct {
	AfterSaleSerialNumber string
	Occurred              time.Time
	Success               bool
}

// DoAfterWxpayProfitReturnFinish wxpay profit return finish
func (s *LedgerServiceImp) DoAfterWxpayProfitReturnFinish(ctx context.Context, q *sqlc.Queries, p DoAfterWxpayProfitReturnFinishParam) (ledgerRollbackPtr *sqlc.LedgerRollback, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	ledgerRollback, err := q.GetLedgerRollbackByAfterSaleSerialNumber(ctx, p.AfterSaleSerialNumber)
	if err != nil {
		return
	}
	slog.Infof("1、获取分账回退 ok")
	// if ledgerRollback.LedgerRollbackStatus != string(sqlc.EmLedgerRollbackStatusPending) {
	// 	err = fmt.Errorf("ledgerRollback 状态不一致:%s", ledgerRollback.LedgerRollbackStatus)
	// 	return
	// }
	if p.Success {
		err = s.finishRollback(ctx, q, p.AfterSaleSerialNumber, p.Occurred)
		if err != nil {
			return
		}
		slog.Infof("2、完成本地分账回退 ok")
	} else {
		err = s.closeRollback(ctx, q, p.AfterSaleSerialNumber)
		if err != nil {
			return
		}
		slog.Infof("2、关闭本地分账回退 ok")
	}

	ledgerRollbackPtr = &ledgerRollback
	return
}

// submitProfitReturn wxpay提交分账回退请求
func (s *LedgerServiceImp) submitProfitReturn(ctx context.Context, ledgerRollbackPtr *sqlc.LedgerRollback, returnMchId string) (wxLedgerReturnNo string, err error) {
	// slog := util.NewContextLogger(ctx, s.slog)
	cBrand := s.cacher.FetchBrand(ledgerRollbackPtr.BrandID)
	if cBrand == nil {
		err = fmt.Errorf("not found brand of brandID:%d", ledgerRollbackPtr.BrandID)
		return
	}

	sgID := cBrand.SgID

	// 此处分账是将佣金划入平台资金账户
	req := &wxpay.ProfitReturnRequest{
		BrandId:     strconv.FormatInt(sgID, 10),                 // 这里的wxpay里的brandId其实是原来的sg_id
		OutOrderNo:  ledgerRollbackPtr.LedgerRequestSerialNumber, //从订单编号改为关联的分账请求编号
		OutReturnNo: ledgerRollbackPtr.SerialNumber,
		ReturnMchId: returnMchId,
		Amount:      ledgerRollbackPtr.SubmchProfitAmount,
		Description: "订单售后退款",
	}

	s.slog.Infof("wxpay.submitProfitReturn req:%+v", req)

	ctxWithTimout, cancel := s.proxy.NewContextWith(ctx)
	defer cancel()
	res, err := s.proxy.WxpayCl.ProfitReturnSubmit(ctxWithTimout, req)
	if err != nil {
		return
	}
	wxLedgerReturnNo = res.WxReturnNo
	return
}

// MarkLedgerRollbackAccounted 标记订单奖励金分账回退已记账
func (s *LedgerServiceImp) MarkLedgerRollbackAccounted(ctx context.Context, q *sqlc.Queries, ledgerRollbackPtr *sqlc.LedgerRollback) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)
	err = q.MarkLedgerRollbackAccountedByAfterSaleSerialNumber(ctx, ledgerRollbackPtr.AfterSaleSerialNumber)
	if err != nil {
		return
	}
	slog.Infof("1 标记订单奖励金分账回退已记账 ok")
	return
}
